package com.itcast.database.smooth.service;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.itcast.database.smooth.config.*;
import lombok.extern.log4j.Log4j2;
import org.apache.shardingsphere.core.config.DataSourceConfiguration;
import org.apache.shardingsphere.orchestration.internal.registry.config.event.DataSourceChangedEvent;
import org.apache.shardingsphere.orchestration.reg.listener.DataChangedEvent;
import org.apache.shardingsphere.shardingjdbc.orchestration.internal.datasource.OrchestrationShardingDataSource;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * 动态分片服务实现
 */
@Component
@Log4j2
public class DynamicShardingService {
    
    /**
     * 记录订单表的分片规则
     */
    public static String SHARDING_RULE_TABLE_ORDER = "";
    
    /**
     * 记录数据源的分库规则
     */
    public static String SHARDING_RULE_DATASOURCE = "";
    
    /**
     * 动态修改分片规则
     *
     * @param mod
     */
    public void dynamicSharding(int mod) {
        
        ShardingDataSourceRule.MOD = mod;
        String newRule = DatasourceEnum.DATASOURCE_PREFIX.getValue() + "${0.." + (mod - 1) + "}";
        if (mod == 1) {
            // 只有一个数据源， 不需要动态表达式
            newRule = DatasourceEnum.DATASOURCE_1.getValue();
            //获取Sharding map数据源
            OrchestrationShardingDataSource dataSource = SpringContextUtil.getBean("tradeSystemDataSource", OrchestrationShardingDataSource.class);
            Map<String, DataSource> dataSourceMap = dataSource.getDataSource().getDataSourceMap();
            DataSource dataSource1 = dataSourceMap.get(DatasourceEnum.DATASOURCE_1.getValue());
            dataSourceMap.clear();
            dataSourceMap.put(DatasourceEnum.DATASOURCE_1.getValue(), dataSource1);
            
        } else {
            
            // 动态数据源配置实现扩容
            Properties properties = loadPropertiesFile("datasource1.properties");
            try {
                log.info("load datasource config url: " + properties.get("url"));
                DruidDataSource druidDataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);
                druidDataSource.setRemoveAbandoned(true);
                druidDataSource.setRemoveAbandonedTimeout(600);
                druidDataSource.setLogAbandoned(true);
                // 设置数据源错误重连时间
                druidDataSource.setTimeBetweenConnectErrorMillis(60000);
                druidDataSource.init();
                OrchestrationShardingDataSource dataSource = SpringContextUtil.getBean("tradeSystemDataSource", OrchestrationShardingDataSource.class);
                Map<String, DataSource> dataSourceMap = dataSource.getDataSource().getDataSourceMap();
                dataSourceMap.put(DatasourceEnum.DATASOURCE_2.getValue(), druidDataSource);
                
                Map<String, DataSourceConfiguration> dataSourceConfigMap = new HashMap<String, DataSourceConfiguration>();
                for (String key : dataSourceMap.keySet()) {
                    dataSourceConfigMap.put(key, DataSourceConfiguration.getDataSourceConfiguration(dataSourceMap.get(key)));
                }
                String result = SHARDING_RULE_TABLE_ORDER.replace(SHARDING_RULE_DATASOURCE, newRule);
                replaceActualDataNodes(result);
                SHARDING_RULE_DATASOURCE = newRule;
                
                dataSource.renew(new DataSourceChangedEvent(
                        "/" + DruidSystemDataSourceConfiguration.DYNAMIC_SHARDING
                                + "/config/schema/logic_db/datasource",
                        dataSourceConfigMap));
                return;
                
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
            
        }
        String result = SHARDING_RULE_TABLE_ORDER.replace(SHARDING_RULE_DATASOURCE, newRule);
        replaceActualDataNodes(result);
        SHARDING_RULE_DATASOURCE = newRule;
        
    }
    
    /**
     * 加载数据源配置文件
     *
     * @param configName
     * @return
     */
    private Properties loadPropertiesFile(String configName) {
        Properties prop = new Properties();
        try {
            InputStream ins = DynamicShardingService.class.getClassLoader().getResourceAsStream(configName);
            prop.load(ins);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
        return prop;
    }
    
    /**
     * 替换sharding里的分片规则
     */
    public void replaceActualDataNodes(String newRule) {
        // 获取已有的配置
        String rules = LocalRegistryCenter.values
                .get("/" + DruidSystemDataSourceConfiguration.DYNAMIC_SHARDING + "/config/schema/logic_db/rule");
        // 修改规则
        String rule = rules.replace(SHARDING_RULE_TABLE_ORDER, newRule);
        LocalRegistryCenter.listeners.get("/" + DruidSystemDataSourceConfiguration.DYNAMIC_SHARDING + "/config/schema")
                .onChange(new DataChangedEvent(
                        "/" + DruidSystemDataSourceConfiguration.DYNAMIC_SHARDING + "/config/schema/logic_db/rule",
                        rule, DataChangedEvent.ChangedType.UPDATED));
        LocalRegistryCenter.values.put("/" + DruidSystemDataSourceConfiguration.DYNAMIC_SHARDING + "/config/schema/logic_db/rule", rule);
        SHARDING_RULE_TABLE_ORDER = newRule;
        
    }
}
